linalg_cholesky.f90 Source File


Source Code

module linalg_cholesky
    use iso_fortran_env, only : int32, real64
    use linalg_errors
    use lapack
    use qrupdate
    implicit none
    private
    public :: cholesky_factor
    public :: cholesky_rank1_update
    public :: cholesky_rank1_downdate
    public :: solve_cholesky

    interface cholesky_factor
        module procedure :: cholesky_factor_dbl
        module procedure :: cholesky_factor_cmplx
    end interface

    interface cholesky_rank1_update
        module procedure :: cholesky_rank1_update_dbl
        module procedure :: cholesky_rank1_update_cmplx
    end interface

    interface cholesky_rank1_downdate
        module procedure :: cholesky_rank1_downdate_dbl
        module procedure :: cholesky_rank1_downdate_cmplx
    end interface

    interface solve_cholesky
        module procedure :: solve_cholesky_mtx
        module procedure :: solve_cholesky_mtx_cmplx
        module procedure :: solve_cholesky_vec
        module procedure :: solve_cholesky_vec_cmplx
    end interface
contains
! ------------------------------------------------------------------------------
pure function cholesky_factor_dbl(a, upper) result(rst)
    !! Computes the Cholesky factorization of a symmetric, positive definite
    !! matrix.
    real(real64), intent(in), dimension(:,:) :: a
        !! The N-by-N matrix to factor.
    logical, intent(in), optional :: upper
        !! An optional input that, if specified, provides control over whether
        !! the factorization is computed as \(A = U^T U\) (set to true), or
        !! as \(A = L L^T\) (set to false).  The default is true such that
        !! \(A = U^T U\).
    real(real64), allocatable, dimension(:,:) :: rst
        !! The factored matrix.

    ! Parameters
    real(real64), parameter :: zero = 0.0d0

    ! Local Variables
    character :: uplo
    integer(int32) :: i, n, flag

    ! Initialization
    n = size(a, 1)
    if (present(upper)) then
        if (upper) then
            uplo = 'U'
        else
            uplo = 'L'
        end if
    else
        uplo = 'U'
    end if

    ! Input Check
    if (size(a, 2) /= n) then
        error stop 1
    end if

    ! Process
    allocate(rst(n, n), source = a)
    call DPOTRF(uplo, n, rst, n, flag)
    if (flag > 0) then
        error stop LA_MATRIX_FORMAT_ERROR
    end if

    ! Zero out the non-used upper or lower diagonal
    if (uplo == 'U') then
        ! Zero out the lower
        do i = 1, n - 1
            rst(i+1:n,i) = zero
        end do
    else
        ! Zero out the upper
        do i = 2, n
            rst(1:i-1,i) = zero
        end do
    end if
end function

! ------------------------------------------------------------------------------
pure function cholesky_factor_cmplx(a, upper) result(rst)
    !! Computes the Cholesky factorization of a symmetric, positive definite
    !! matrix.
    complex(real64), intent(in), dimension(:,:) :: a
        !! The N-by-N matrix to factor.
    logical, intent(in), optional :: upper
        !! An optional input that, if specified, provides control over whether
        !! the factorization is computed as \(A = U^H U\) (set to true), or
        !! as \(A = L L^H\) (set to false).  The default is true such that
        !! \(A = U^H U\).
    complex(real64), allocatable, dimension(:,:) :: rst
        !! The factored matrix.

    ! Parameters
    complex(real64), parameter :: zero = (0.0d0, 0.0d0)

    ! Local Variables
    character :: uplo
    integer(int32) :: i, n, flag

    ! Initialization
    n = size(a, 1)
    if (present(upper)) then
        if (upper) then
            uplo = 'U'
        else
            uplo = 'L'
        end if
    else
        uplo = 'U'
    end if

    ! Input Check
    if (size(a, 2) /= n) then
        error stop 1
    end if

    ! Process
    allocate(rst(n, n), source = a)
    call ZPOTRF(uplo, n, rst, n, flag)
    if (flag > 0) then
        ! ERROR: Matrix is not positive definite
        error stop LA_MATRIX_FORMAT_ERROR
    end if

    ! Zero out the non-used upper or lower diagonal
    if (uplo == 'U') then
        ! Zero out the lower
        do i = 1, n - 1
            rst(i+1:n,i) = zero
        end do
    else
        ! Zero out the upper
        do i = 2, n
            rst(1:i-1,i) = zero
        end do
    end if
end function

! ------------------------------------------------------------------------------
pure subroutine cholesky_rank1_update_dbl(r, u)
    !! Computes the rank 1 update to a Cholesky factored matrix \(A = R^T R\) 
    !! such that \(A_1 = A + \vec{u} \vec{u}^T\).
    real(real64), intent(inout), dimension(:,:) :: r
        !! On input, the N-by-N upper triangular matrix \(R\).  On output, the 
        !! updated matrix \(R_1\).
    real(real64), intent(inout), dimension(:) :: u
        !! On input, the N-element vector \(\vec{u}\).  On output, the rotation
        !! sines used to transform \(R\) to \(R_1\).

    ! Local Variables
    integer(int32) :: n, lwork
    real(real64), allocatable, dimension(:) :: w

    ! Initialization
    n = size(r, 1)
    lwork = n

    ! Input Check
    if (size(r, 2) /= n) then
        error stop 1
    else if (size(u) /= n) then
        error stop 2
    end if

    ! Local Memory Allocation
    allocate(w(lwork))

    ! Process
    call DCH1UP(n, r, n, u, w)
end subroutine

! ------------------------------------------------------------------------------
pure subroutine cholesky_rank1_update_cmplx(r, u)
    !! Computes the rank 1 update to a Cholesky factored matrix \(A = R^H R\) 
    !! such that \(A_1 = A + \vec{u} \vec{u}^H\).
    complex(real64), intent(inout), dimension(:,:) :: r
        !! On input, the N-by-N upper triangular matrix \(R\).  On output, the 
        !! updated matrix \(R_1\).
    complex(real64), intent(inout), dimension(:) :: u
        !! On input, the N-element vector \(\vec{u}\).  On output, the rotation
        !! sines used to transform \(R\) to \(R_1\).

    ! Local Variables
    integer(int32) :: n, lwork
    real(real64), allocatable, dimension(:) :: w

    ! Initialization
    n = size(r, 1)
    lwork = n

    ! Input Check
    if (size(r, 2) /= n) then
        error stop 1
    else if (size(u) /= n) then
        error stop 2
    end if

    ! Local Memory Allocation
    allocate(w(lwork))

    ! Process
    call ZCH1UP(n, r, n, u, w)
end subroutine

! ------------------------------------------------------------------------------
pure subroutine cholesky_rank1_downdate_dbl(r, u)
    !! Computes the rank 1 downdate to a Cholesky factored matrix \(A = R^T R\) 
    !! such that \(A_1 = A - \vec{u} \vec{u}^T\).  This operation only works if
    !! the new matrix \(A_1\) is positive definite.
    real(real64), intent(inout), dimension(:,:) :: r
        !! On input, the N-by-N upper triangular matrix \(R\).  On output, the 
        !! updated matrix \(R_1\).
    real(real64), intent(inout), dimension(:) :: u
        !! On input, the N-element vector \(\vec{u}\).  On output, the rotation
        !! sines used to transform \(R\) to \(R_1\).

    ! Local Variables
    integer(int32) :: n, lwork, flag
    real(real64), allocatable, dimension(:) :: w

    ! Initialization
    n = size(r, 1)
    lwork = n

    ! Input Check
    if (size(r, 2) /= n) then
        error stop 1
    else if (size(u) /= n) then
        error stop 2
    end if

    ! Local Memory Allocation
    allocate(w(lwork))

    ! Process
    call DCH1DN(n, r, n, u, w, flag)
    if (flag == 1) then
        ! ERROR: The matrix is not positive definite
        error stop LA_MATRIX_FORMAT_ERROR
    else if (flag == 2) then
        ! ERROR: The matrix is singular
        error stop LA_SINGULAR_MATRIX_ERROR
    end if
end subroutine

! ------------------------------------------------------------------------------
pure subroutine cholesky_rank1_downdate_cmplx(r, u)
    !! Computes the rank 1 downdate to a Cholesky factored matrix \(A = R^H R\) 
    !! such that \(A_1 = A - \vec{u} \vec{u}^H\).  This operation only works if
    !! the new matrix \(A_1\) is positive definite.
    complex(real64), intent(inout), dimension(:,:) :: r
        !! On input, the N-by-N upper triangular matrix \(R\).  On output, the 
        !! updated matrix \(R_1\).
    complex(real64), intent(inout), dimension(:) :: u
        !! On input, the N-element vector \(\vec{u}\).  On output, the rotation
        !! sines used to transform \(R\) to \(R_1\).

    ! Local Variables
    integer(int32) :: n, lwork, flag
    real(real64), allocatable, dimension(:) :: w

    ! Initialization
    n = size(r, 1)
    lwork = n

    ! Input Check
    if (size(r, 2) /= n) then
        error stop 1
    else if (size(u) /= n) then
        error stop 2
    end if

    ! Local Memory Allocation
    allocate(w(lwork))

    ! Process
    call ZCH1DN(n, r, n, u, w, flag)
    if (flag == 1) then
        ! ERROR: The matrix is not positive definite
        error stop LA_MATRIX_FORMAT_ERROR
    else if (flag == 2) then
        ! ERROR: The matrix is singular
        error stop LA_SINGULAR_MATRIX_ERROR
    end if
end subroutine

! ------------------------------------------------------------------------------
pure function solve_cholesky_mtx(upper, a, b) result(x)
    !! Solves the system of Cholesky factored equations \(A X = R^T R X = B\) or
    !! \(A X = L L^T X = B\).
    logical, intent(in) :: upper
        !! Set to true if \(A\) is factored such that \(A = R^T R\); else, set
        !! to false if \(A\) is factored such that \(A = L L^T\).
    real(real64), intent(in), dimension(:,:) :: a
        !! The N-by-N Cholesky factored matrix as returned by cholesky_factor.
    real(real64), intent(in), dimension(:,:) :: b
        !! The N-by-NRHS matrix \(B\).
    real(real64), allocatable, dimension(:,:) :: x 
        !! The resulting N-by-NRHS matrix \(X\).

    ! Local Variables
    character :: uplo
    integer(int32) :: n, nrhs, flag

    ! Initialization
    n = size(a, 1)
    nrhs = size(b, 2)
    if (upper) then
        uplo = 'U'
    else
        uplo = 'L'
    end if

    ! Input Check
    if (size(a, 2) /= n) then
        error stop 1
    else if (size(b, 1) /= n) then
        error stop 2
    end if

    ! Process
    allocate(x(n,nrhs), source = b)
    call DPOTRS(uplo, n, nrhs, a, n, x, n, flag)
end function

! ------------------------------------------------------------------------------
pure function solve_cholesky_mtx_cmplx(upper, a, b) result(x)
    !! Solves the system of Cholesky factored equations \(A X = R^H R X = B\) or
    !! \(A X = L L^H X = B\).
    logical, intent(in) :: upper
        !! Set to true if \(A\) is factored such that \(A = R^H R\); else, set
        !! to false if \(A\) is factored such that \(A = L L^H\).
    complex(real64), intent(in), dimension(:,:) :: a
        !! The N-by-N Cholesky factored matrix as returned by cholesky_factor.
    complex(real64), intent(in), dimension(:,:) :: b
        !! The N-by-NRHS matrix \(B\).
    complex(real64), allocatable, dimension(:,:) :: x
        !! The resulting N-by-NRHS matrix \(X\).

    ! Local Variables
    character :: uplo
    integer(int32) :: n, nrhs, flag

    ! Initialization
    n = size(a, 1)
    nrhs = size(b, 2)
    if (upper) then
        uplo = 'U'
    else
        uplo = 'L'
    end if

    ! Input Check
    if (size(a, 2) /= n) then
        error stop 1
    else if (size(b, 1) /= n) then
        error stop 2
    end if

    ! Process
    allocate(x(n,nrhs), source = b)
    call ZPOTRS(uplo, n, nrhs, a, n, x, n, flag)
end function

! ------------------------------------------------------------------------------
pure function solve_cholesky_vec(upper, a, b) result(x)
    !! Solves the system of Cholesky factored equations 
    !! \(A \vec{x} = R^T R \vec{x} = \vec{b}\) or
    !! \(A \vec{x} = L L^T \vec{x} = \vec{b}\).
    logical, intent(in) :: upper
        !! Set to true if \(A\) is factored such that \(A = R^T R\); else, set
        !! to false if \(A\) is factored such that \(A = L L^T\).
    real(real64), intent(in), dimension(:,:) :: a
        !! The N-by-N Cholesky factored matrix as returned by cholesky_factor.
    real(real64), intent(in), dimension(:) :: b
        !! The N-element vector \(\vec{b}\).
    real(real64), allocatable, dimension(:) :: x
        !! The resulting N-element vector \(\vec{x}\).

    ! Local Variables
    character :: uplo
    integer(int32) :: n, flag

    ! Initialization
    n = size(a, 1)
    if (upper) then
        uplo = 'U'
    else
        uplo = 'L'
    end if

    ! Input Check
    if (size(a, 2) /= n) then
        error stop 1
    else if (size(b) /= n) then
        error stop 2
    end if

    ! Process
    allocate(x(n), source = b)
    call DPOTRS(uplo, n, 1, a, n, x, n, flag)
end function

! ------------------------------------------------------------------------------
pure function solve_cholesky_vec_cmplx(upper, a, b) result(x)
    !! Solves the system of Cholesky factored equations 
    !! \(A \vec{x} = R^H R \vec{x} = \vec{b}\) or
    !! \(A \vec{x} = L L^H \vec{x} = \vec{b}\).
    logical, intent(in) :: upper
        !! Set to true if \(A\) is factored such that \(A = R^H R\); else, set
        !! to false if \(A\) is factored such that \(A = L L^H\).
    complex(real64), intent(in), dimension(:,:) :: a
        !! The N-by-N Cholesky factored matrix as returned by cholesky_factor.
    complex(real64), intent(in), dimension(:) :: b
        !! The N-element vector \(\vec{b}\).
    complex(real64), allocatable, dimension(:) :: x
        !! The resulting N-element vector \(\vec{x}\).

    ! Local Variables
    character :: uplo
    integer(int32) :: n, flag

    ! Initialization
    n = size(a, 1)
    if (upper) then
        uplo = 'U'
    else
        uplo = 'L'
    end if

    ! Input Check
    if (size(a, 2) /= n) then
        error stop 1
    else if (size(b) /= n) then
        error stop 2
    end if

    ! Process
    allocate(x(n), source = b)
    call ZPOTRS(uplo, n, 1, a, n, x, n, flag)
end function

! ------------------------------------------------------------------------------
end module